package Carte;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.GregorianCalendar;
import java.util.Iterator;

import Serveur.RechercheTransport;
import Serveur.Serveur;
import Services.ServiceChoisis;
import Transports.Transports;


/**
 * Class descriptif d'un trajet
 * 
 * @author Fabien
 * 
 */
public class Trajet implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	//Identifiant pour un trajet 
	int IDTrajet;
	
	//Villes de depart et d'arrive
    private Ville villeDepart;
    private Ville villeArrive;

    //Date de dapart et d'arrive
    private GregorianCalendar datedepart;
    private GregorianCalendar datearrive;

    private int placeOcupper;

    // Liste des transports
    private int idTransport;
    
    //Sauvegarder le serveur 
    Serveur serv;
    
    //Distance enter les ville
    private int distance;

    /**
     * Constructeur normal
     *  Le trajet est directement ajouté dans le serveur
     *  
     * @param villeDepart
     * @param villeArrive
     * @param datedepart
     * @param datearrive
     * @param transport
     *
     */
    public Trajet(Serveur serv, Ville villeDepart, Ville villeArrive,
            GregorianCalendar datedepart, GregorianCalendar datearrive,
            int transport, int distance) {

        this.villeDepart = villeDepart;
        this.villeArrive = villeArrive;
        this.datedepart = datedepart;
        this.datearrive = datearrive;
        this.idTransport = transport;
        placeOcupper = 0;
        this.distance = distance;
        IDTrajet = serv.getIdentifiantValideTrajets();
        this.serv = serv;
        
        serv.addTrajet(this);
    }
    /**
     * Constructeur pour la sérialisation
     * (permet de garder les identifiants d'une session à une autre)
     * Le trajet n'est pas ajouté automatiquement au serveur
     * 
     * @param serv
     * @param villeDepart
     * @param villeArrive
     * @param datedepart
     * @param datearrive
     * @param transport
     * @param distance
     * @param IDTrajet
     */
    public Trajet(Serveur serv, Ville villeDepart, Ville villeArrive,
            GregorianCalendar datedepart, GregorianCalendar datearrive,
            int transport, int distance, int IDTrajet) {

        this.villeDepart = villeDepart;
        this.villeArrive = villeArrive;
        this.datedepart = datedepart;
        this.datearrive = datearrive;
        this.idTransport = transport;
        placeOcupper = 0;
        this.distance = distance;
        this.IDTrajet = IDTrajet;
        this.serv = serv;
        
    }
    public int getPlaceOcupper() {
        return placeOcupper;
    }

    public GregorianCalendar getDatedepart() {
        return datedepart;
    }

    public GregorianCalendar getDatearrive() {
        return datearrive;
    }

    @Override
    public String toString() {
        String retour;
        retour = "\nTransport : " + serv.getTransportListe(idTransport).getTypeDuTransport();
        retour += "\nPlaces disponibles : " + this.getNbPlaceDispo(); 
        retour += "\nVille depart/Arrivee : " + villeDepart + " / " + villeArrive;
        retour += "\nDate depart/arrivee : " + displayDate(datedepart) + " / " + displayDate(datearrive);
        retour += "\n";

        return retour;
    }

    public String displayDate(GregorianCalendar date) {
        return (Calendrier.toString(date));
    }

    public Ville getVilleDepart() {
        return villeDepart;
    }

    public Ville getVilleArrive() {
        return villeArrive;
    }

    public void print() {
        System.out.println(toString());
    }

    public int getTransport() {
        return idTransport;
    }

    public int getIDTrajet() {
		return IDTrajet;
	}

	public void setIDTrajet(int iDTrajet) {
		IDTrajet = iDTrajet;
	}

	/**
     * Verifier si le nombre de place est assez importante pour reserver un
     * certain nombre de personne
     * 
     * @param nbPlace
     * @return
     */
    public boolean peutReserver(int nbPlace) {
    	//Recuperer le transport dans le serveur
    	//
    	Transports transport = serv.getTransportListe(idTransport);
    	
        if ((placeOcupper + nbPlace) <= transport.getCapacite()) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Reserver le nombre de place demandee, il faut augmenter le nombre de place
     * occupees.
     * 
     * @param nbplace
     * @return
     */
    public boolean reserver(int nbplace) {
        if (peutReserver(nbplace)) {
            placeOcupper += nbplace;
            return true;
        } else {
            return false;
        }
    }

    /**
     * Enlever le nombre de passagers reserves
     * 
     * @param nb
     * @return
     */
    public boolean annulerReservation(int nb) {
        if (nb < 0) {
            return false;
        }

        if ((placeOcupper - nb) < 0) {
            return false;
        }

        placeOcupper = placeOcupper - nb;
        return true;
    }

    /**
     * Recuperer le nombre de place dispo
     * 
     * @return the nomber of free place
     */
    public int getNbPlaceDispo() {
    	Transports transport = serv.getTransportListe(idTransport);
        int capacite = transport.getCapacite();
        return capacite - placeOcupper;
    }

    /**
     * Recherche de trajet par ville, par date et par transport et selon les
     * places disponibles
     * 
     * @param ListeTrajetsarg ne peut pas etre null
     * @param villeDepart peut etre null
     * @param villeArrive peut etre null
     * @param dateDepart puet etre null
     * @param dateArrive peut etre null
     * @param IdTransport peut etre a -1
     * @param nbPersonne peut etre a 0
     * @return
     */

	public static ArrayList<Trajet> searchTrajet(
            ArrayList<Trajet> ListeTrajetsarg, Ville villeDepart,
            Ville villeArrive, GregorianCalendar dateDepart,
            GregorianCalendar dateArrive, int IdTransport, int nbPersonne) {
 
        // Verifier que la liste des trajets n'est pas nulle
        if (ListeTrajetsarg == null) {
            return null;
        }		
		
    	// Travailler sur une copie de la liste 
    	//
        @SuppressWarnings("unchecked")
		ArrayList<Trajet> ListeTrajetRetour = (ArrayList<Trajet>) ListeTrajetsarg.clone();



        // Si la ville de depart n'est pas null
        if (villeDepart != null) {
            Iterator<Trajet> it = ListeTrajetsarg.iterator();
            while (it.hasNext()) {
                Trajet current = it.next();
                if (!current.getVilleDepart().equals(villeDepart)) {
                    ListeTrajetRetour.remove(current);
                }
            }
        }

        // Si la ville d'arrive n'est pas null
        if (villeArrive != null) {
            Iterator<Trajet> it = ListeTrajetsarg.iterator();
            while (it.hasNext()) {
                Trajet current = it.next();
                if (!current.getVilleArrive().equals(villeArrive)) {
                    ListeTrajetRetour.remove(current);
                }
            }
        }

        // Si la date de depart n'est pas null
        if (dateDepart != null) {
            Iterator<Trajet> it = ListeTrajetsarg.iterator();
            while (it.hasNext()) {
                Trajet current = it.next();
                if (!current.getDatedepart().equals(dateDepart)) {
                    ListeTrajetRetour.remove(current);
                }
            }
        }

        // Si la date d'arrive n'est pas null
        if (dateArrive != null) {
            Iterator<Trajet> it = ListeTrajetsarg.iterator();
            while (it.hasNext()) {
                Trajet current = it.next();
                if (!current.getDatearrive().equals(dateArrive)) {
                    ListeTrajetRetour.remove(current);
                }
            }
        }

        // Si l'identifiant du depart n'est pas null
        if (IdTransport != -1) {
            Iterator<Trajet> it = ListeTrajetsarg.iterator();
            while (it.hasNext()) {
                Trajet current = it.next();
                if ((current.getIdTransport() != IdTransport)) {
                    ListeTrajetRetour.remove(current);
                }
            }
        }

        if (nbPersonne > 0) {
            Iterator<Trajet> it = ListeTrajetsarg.iterator();
            while (it.hasNext()) {
                Trajet current = it.next();
                if (!current.peutReserver(nbPersonne)) {
                    // Supprimer les trajets dans lesquels il n'y a plus de
                    // place diponible.
                	
                    ListeTrajetRetour.remove(current);
                }
            }
        }

        // Rechercher dans les trajets
        if (ListeTrajetsarg.size() == ListeTrajetRetour.size()
                && villeArrive != null && villeDepart != null) {
            @SuppressWarnings("unchecked")
			ArrayList<Trajet> ListeMod = (ArrayList<Trajet>) ListeTrajetsarg.clone();
            RechercheTransport rech;
            try {
                rech = new RechercheTransport(villeDepart, villeArrive,
                        ListeMod);
            } catch (IllegalArgumentException e) {
                return null;
            }
            if (rech.cheminPossible()) {
                return rech.Chemin();
            }
        }

        return ListeTrajetRetour;
    }

    /**
     * Recuperer le temps necessaire pour effectuer le trajet
     * 
     * @return the time for travel
     */
    @SuppressWarnings("deprecation")
	public int getTime() {
        int h1 = ((datearrive.getGregorianChange().getHours()) * 60)
                + ((datearrive.getGregorianChange().getMinutes()));
        int h2 = (datedepart.getGregorianChange().getHours()) * 60
                + ((datedepart.getGregorianChange().getMinutes()));
        return h1 - h2;
    }
    
    
    /**
     * Redefinition de la methode equals
     */
    @Override
    public boolean equals(Object obj) {
    	if(obj == null){
    		return false;
    	}
    	
        Trajet t = (Trajet)obj;
        
        if(t.getIDTrajet() == this.getIDTrajet()) return true;
        
        if(!t.getDatearrive().equals(this.getDatearrive())){
            return false;
        }
        if(!t.getDatedepart().equals(this.getDatedepart())){
            return false;
        }
        if(!(t.idTransport == this.idTransport)){
            return false;
        }
        if(!t.getVilleArrive().equals(this.getVilleArrive())){
            return false;
        }
        if(!t.getVilleDepart().equals(this.getVilleDepart())){
        	return false;
           
        }
        
        return true;
    }
    
    /**
     * Calculer le prix du billet d'un trajet
     * qui depend de la distance et du prix au kilometre pour un transport donnée
     */
    public double getPrixBillet(){
    	Transports transport = serv.getTransportListe(idTransport);
    	double prixTot = transport.getPrixBillet() + distance*transport.getPrixAuKilometre();
    	return prixTot;
    }
    
    public int getIdTransport() {
		return idTransport;
	}

	/**
     * Afficher les informations d'un trajet
     */
    public void infoTrajet(){
    	String info;
    	info = "Distance entre " + villeDepart + " et " + villeArrive + " : " + distance + "\n";
    	info += "Temps du trajet : " + getTime() + "\n";
    }
    
    /**
     * Connaitre le trajet le plus court entre deux villes
     * @param ListeTrajetsarg
     * @param villeDepart
     * @param villeArrive
     * @return
     */
    public static Trajet PlusCourtTrajet(
            ArrayList<Trajet> ListeTrajetsarg, Ville villeDepart,
            Ville villeArrive) {
    	
    	//recuperer la liste des trajet correspondants a ces données
    	ArrayList<Trajet> ListeTrajetTemp = searchTrajet(ListeTrajetsarg, villeDepart, villeArrive, null, null, 0, 0);
    	
    	Trajet plusCourt = null;
    	plusCourt = ListeTrajetTemp.get(0);
    	
    	Iterator<Trajet> i = ListeTrajetTemp.iterator();
    	while(i.hasNext()){
    		Trajet temp = i.next();
    		if(temp.getDistance() < plusCourt.getDistance()){
    			plusCourt = temp;
    		}
    	}
    	
    	return plusCourt;
    }
    
    /**
     * Connaitre le trajet le plus court entre deux villes
     * @param ListeTrajetsarg
     * @param villeDepart
     * @param villeArrive
     * @return
     */
    public static Trajet PlusRapideTrajet(ArrayList<Trajet> ListeTrajetsarg, Ville villeDepart,
            Ville villeArrive){
    	//recuperer la liste des trajet correspondants a ces données
    	ArrayList<Trajet> ListeTrajetTemp = searchTrajet(ListeTrajetsarg, villeDepart, villeArrive, null, null, 0, 0);
    	
    	Trajet plusRapide = null;
    	plusRapide = ListeTrajetTemp.get(0);
    	
    	Iterator<Trajet> i = ListeTrajetTemp.iterator();
    	while(i.hasNext()){
    		Trajet temp = i.next();
    		if(temp.getTime() < plusRapide.getTime()){
    			plusRapide = temp;
    		}
    	}
    	
    	return plusRapide;
    }

    /**
     * Trouver le prix le moins cher entre deux villes et pour certains
     * services choisis.
     * 
     * @param ListeTrajetsarg
     * @param villeDepart
     * @param villeArrive
     * @param services
     * @return
     */
    public static Trajet MoinCherUnePersonne(ArrayList<Trajet> ListeTrajetsarg, Ville villeDepart,
            Ville villeArrive, ServiceChoisis services){
    	
    	//recuperer la liste des trajet correspondants a ces données
    	ArrayList<Trajet> ListeTrajetTemp = searchTrajet(ListeTrajetsarg, villeDepart, villeArrive, null, null, 0, 0);
    	
    	services.getPrixServices();
    	
    	Trajet moinsCher = null;
    	moinsCher = ListeTrajetTemp.get(0);
    	
    	Iterator<Trajet> i = ListeTrajetTemp.iterator();
    	while(i.hasNext()){
    		Trajet temp = i.next();
    		
    		//Calculer prix du billet
    		double prixtemp = temp.getPrixBillet() + services.getPrixServices();
    		double prix = moinsCher.getPrixBillet() + services.getPrixServices();
    		
    		if(prixtemp < prix){
    			moinsCher = temp;
    		}
    	}
    	
    	return moinsCher;
    }
    
    /**
     * Obtenir la distance pour le trajet
     * @return
     */
	public int getDistance() {
		return distance;
	}
    
	public Serveur getServeur()
	{
		return serv;
	}
	
	public void setPlaceOcupper(int placeOcupper) {
		this.placeOcupper = placeOcupper;
	}
}
